package jgrit.gui.export;

import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import jgrit.JGritProperties;
import jgrit.control.ExportException;
import jgrit.control.ExportService;
import jgrit.gui.FileSelector;
import jgrit.options.Option;
import jgrit.options.OptionCollection;
import static jgrit.Constants.*;

/**
 * This superclass covers all common code between the two export dialogs.
 * 
 * @author Derek Olejnik
 */
public abstract class ExportDialog extends JDialog {
	private static final long serialVersionUID = -3936311191128058730L;

	/***************************************************************************
	 * 								 VARIABLES
	 **************************************************************************/
	
	protected JButton okButton, cancelButton;
	protected OptionCollection SETTINGS;
	protected ExportDialog otherGUI;
	protected final JGritProperties PROPERTIES = JGritProperties.getInstance();
	
	/**
	 * Object that will handle selecting image files
	 */
	private FileSelector fileSelector = new FileSelector();
	
	protected static ExportDialog inst = null;
	
	/***************************************************************************
	 * 							  SET LOOK & FEEL
	 **************************************************************************/
	
	static {
		if (!System.getProperty("os.name").startsWith("Mac")) {
			// Set Look & Feel on Linux and Windows
			try {
				UIManager.setLookAndFeel("com.jgoodies.looks.plastic.PlasticXPLookAndFeel");
			} catch (Exception e) {
				// Don't set a L&F
			}
		}
	}

	/***************************************************************************
	 * 							    CONSTRUCTOR
	 **************************************************************************/
	
	/**
	 * If we are calling the basic constructor, we haven't imported a file
	 */
	public ExportDialog() {
		super();
		
		// Configure a handler to print any exceptions that aren't handled at all
		Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
			public void uncaughtException(Thread t, Throwable e) {
				displayError(
						"Internal Error",
						"An unexpected internal error occurred:", e);
			}
		});
		
		String selectedFile = fileSelector.selectImageFile(null);
		if (selectedFile == null) {
			// They didn't select a file to import, so we don't start
			System.exit(0);
		}
		
		//Use the selected file as the input file
		List<File> files = new ArrayList<File>();
		files.add(new File(selectedFile));
		
		List<BufferedImage> images = new ArrayList<BufferedImage>();
		for (int i = 0; i < files.size(); i++)
		{
			try {
				BufferedImage decodedImage = ImageIO.read(files.get(i));
				if (decodedImage == null) {
					displayError(
							"Bad image format",
							"Couldn't open the file. It may be corrupt or in an unrecognized format.", null);
					System.exit(0);
				}
				images.add(decodedImage);
			} catch (IOException e) {
				displayError(
						"I/O Error",
						"The image file could not be read:", e);
				System.exit(0);
			}
		}
		
		SETTINGS = new OptionCollection(files, images);
	}
	
	/**
	 * We know a main gui has already been created
	 * @param otherGUI - other gui to link to
	 */
	public ExportDialog(ExportDialog otherGUI) {
		this(otherGUI.SETTINGS, (Frame)otherGUI.getOwner(), null, otherGUI.isModal());
		
		this.otherGUI = otherGUI;
	}
	
	/**
	 * Creates a dialog based upon an already created option collection
	 * @param options - OptionCollection: collection to use
	 */
	public ExportDialog(OptionCollection options, Frame owner, String title,
			boolean modal) {
		super(owner, title, modal);
		SETTINGS = options;
	}
	
	/**
	 * Setup the buttons (Advanced, Ok, and Cancel)
	 */
	protected void initButtons() {
		okButton = new JButton("OK");
		getRootPane().setDefaultButton(okButton);
		cancelButton = new JButton("Cancel");
		
		for (Option restrictedOption:SETTINGS.getRestrictedOptions()) {
			restrictedOption.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					boolean okValid = true;
					for (Option checked:SETTINGS.getRestrictedOptions()) {
						if (!checked.validate()) {
							okValid = false;
							break;
						}
					}
					
					if (okValid) {
						okButton.setEnabled(true);
					} else {
						okButton.setEnabled(false);
					}
				}
			});
		}
		

		okButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				try {
					ExportDialog.this.setVisible(false);
					final ExportOutputWindow statusBox = new ExportOutputWindow("");
					statusBox.setLocationRelativeTo(ExportDialog.this);
					
					/*
					 * Has to occur after the export GUI is set visible,
					 * which may also run on the event dispatching thread
					 */
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							statusBox.setVisible(true);
						}
					});
					
					String exportOutput = ExportService.executeCommand(SETTINGS);
					statusBox.setMsgString(exportOutput);
					// Ending of program handled by grit status box
				} catch (ExportException e) {
					String toolName = e.getUnderlyingToolName();
					ExportDialog.displayError(
							toolName + " Error", 
							"Unexpected error while using " + toolName + 
								" to export the image:", e.getCause());
					System.exit(0);
				}
			}
		});

		cancelButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
//				System.exit(0);
				//ExportDialog.this.dispose();
				ExportDialog.this.setVisible(false);
			}
		});
	}
	
	/**
	 * Displays an error dialog to the user.
	 * <p>
	 * The dialog contains an error message and a description of the
	 * initiating exception (if one is specified).
	 * 
	 * @param dialogTitle	the title of the error dialog.
	 * @param firstLine		the first line of the error dialog's message.
	 * @param e				the exception that prompted the error,
	 * 						or <tt>null</tt> if none did.
	 */
	public static void displayError(String dialogTitle, String firstLine, Throwable e) {
		String errMsg;
		if (e == null) {
			errMsg = firstLine;
		} else {
			errMsg = firstLine + "\n";
			if (e.getMessage() != null) {
				errMsg += e.getMessage();
			} else {
				errMsg += "<no message>";
			}
			errMsg += " (" + e.getClass().getSimpleName() + ")";
		}
		
		if (e != null) {
			// Print exception to error stream
			e.printStackTrace(System.err);
		}
		
		// Display dialog to user
		JOptionPane.showMessageDialog(null, errMsg, dialogTitle, 
				JOptionPane.ERROR_MESSAGE);
	}
	
	/**
	 * Create a new instance of the appropriate GUI (depending on the setting
	 * in the properties file).
	 * 
	 * @param parent Parent component for placement; it is allowed to be null
	 * @return The GUI instance
	 */
	private static ExportDialog createInstance() {
		String mainGUI = JGritProperties.getInstance().getProperty(
				MAIN_GUI_PROPERTY);
		
		if (mainGUI.equals(ADVANCED_GUI)) {
			GBAExportAdvanced inst = new GBAExportAdvanced();
			return inst;
		} else { // Use Task GUI by default if something isn't right
			GBAExportTaskStyle inst = new GBAExportTaskStyle();
			return inst;
		}
	}
	
	/**
	 * Create a new instance of the appropriate GUI (depending on the setting
	 * in the properties file), given a pre-existing OptionCollection.
	 * 
	 * @param owner Parent frame for placement; it is allowed to be null
	 * @param options The OptionCollection to use
	 * @return The GUI instance
	 */
	private static ExportDialog createInstance(Frame owner, OptionCollection options) {
		String mainGUI = JGritProperties.getInstance().getProperty(
				MAIN_GUI_PROPERTY);
		
		/*
		 * Create the dialog as modal if the owner frame is not null
		 */
		if (mainGUI.equals(ADVANCED_GUI)) {
			GBAExportAdvanced inst = new GBAExportAdvanced(options, owner,
					null, owner != null);
			return inst;
		} else { // Use Task GUI by default if something isn't right
			GBAExportTaskStyle inst = new GBAExportTaskStyle(options, owner,
					null, owner != null);
			return inst;
		}
	}
	
	/**
	 * Calling this method will use the properties.xml file to
	 * determine startup GUI, and will give it the OptionCollection passed in.
	 * 
	 * @param owner Parent frame for placement; it is allowed to be null
	 * @param options Option collection to give to GUI
	 */
	public static void showGUI(Frame owner, OptionCollection options) {
		if (inst == null)
			inst = createInstance(owner, options);
		inst.setLocationRelativeTo(owner);
		
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				inst.setVisible(true);
			}
		});
	}
	
	/**
	 * Calling this method will use the properties.xml file to
	 * determine startup GUI
	 * 
	 * @param owner Parent frame for placement; it is allowed to be null
	 * @param options Option collection to give to GUI
	 */
	public static void showGUI(Frame owner) {
		if (inst == null)
			inst = createInstance();
		inst.setLocationRelativeTo(owner);
		
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				inst.setVisible(true);
			}
		});
	}
	
	/**
	 * Calling MainGUI's main method will use the properties.xml file to
	 * determine startup GUI
	 * 
	 * @param args - not used
	 */
	public static void main(String[] args) {
		// NOTE: Careful - any exceptions thrown here will not be reported to the user
		// NOTE: Will throw an IllegalArgumentException on Java 1.5 (but not 1.6+)
		showGUI(null);
	}
}
